#include <sys/socket.h>
#include <netinet/in.h>
#include <syslog.h>
#include <signal.h>
#include <dlfcn.h>
#include <dirent.h>
#include "sip.h"
#include "utils.h"
#include "ice_rtp.h"
#include "xmppconnect.h"
#include "user.h"
#include "length_define.h"

#ifdef MODULE_PATH
	#define MODULE_PATH_QUOTE QUOTEME_FUNC(MODULE_PATH)
#else
	#define MODULE_PATH_QUOTE "/usr/lib/sipxmppserver"
	#error error: MODULE_PATH undefined.
#endif



static void reload_modules();
static int sipxmppgate_stopping;
char *pid_file = NULL;
extern int sipxmpp_log_syslog;
void ex_program(int sig) {
	sipxmppgate_stopping = 1;
}
void reload_config(int sig)
{
	reload_modules();
}

static pid_t sipxmpp_fork()
{
#ifdef FORK_OK
	return fork();
#else
	return __fork();
#endif
}
extern int sip_listen_port;
extern char sip_user_agent[50];
char *modules_path;
int daemon_mode = 0;
int xmpp_getopt(int argc, char *argv[])
{
	int c;
	sip_listen_port = 5060;
	strncpy(sip_user_agent,"sipxmppserver",50);
	modules_path = MODULE_PATH_QUOTE;

#ifndef DISABLE_GETOPT
	while ((c = getopt(argc, argv, "f:p:cu:d")) != -1) {
		switch(c)
		{
			case 'p':
				sip_listen_port = atoi(optarg);
				break;
			case 'f':
				pid_file = optarg;
				break;
			case 'c':
				sipxmpp_log_syslog = 0;
				break;
			case 'u':
				strncpy(sip_user_agent,optarg,50);
				break;
			case 'd':
				daemon_mode = 1;
				break;
			default:
				return 1;
		}
	}
	return 0;
#else //out pap2v2 compile toolchain can not read command line parameters. so we just use default values.
	daemon_mode = 1;
	return 0;
#endif
}
static int help()
{
	fprintf(stdout,"Usage:\n");
	fprintf(stdout,"	sipxmppserver [options]\n");
	fprintf(stdout,"	-f	create a pid file.\n");
	fprintf(stdout,"	-p	sip protocol listen port.\n");
	fprintf(stdout,"	-c	write logs to stdrr instead of syslog.\n");
	fprintf(stdout,"	-d	run in daemon mode.\n");
	return 0;
}
static struct sipxmpp_module
{
	void *handle;
	struct sipxmpp_module *next;
} *sipxmpp_modules = NULL;
static int load_modules()
{
	char *error;
	char full_path[PATH_MAX];
	int (* module_init)();
	struct dirent *file;
	DIR *dir = opendir(modules_path);
	LogD("begin load modules,modules_path=[%s]",modules_path);
	if(dir)
	{
		while ((file = readdir(dir)) != NULL)
		{
			if(!strcasecmp(file->d_name + strlen(file->d_name) - 3,".so"))
			{
				LogD("found module %s\n",file->d_name);
				snprintf(full_path,PATH_MAX,"%s/%s",modules_path,file->d_name);

				struct sipxmpp_module *tmp_module = sipxmpp_calloc(sizeof(struct sipxmpp_module));

				tmp_module->handle = dlopen(full_path, RTLD_LAZY);
				if (!tmp_module->handle)
				{
					LogE("1.unable to load module %s",file->d_name);
					chkndfree(tmp_module);
					continue;
				}
				module_init = dlsym(tmp_module->handle, "mod_init");
				if ((error = dlerror()) != NULL)
				{
					LogE("2.unable to load module %s",file->d_name);
					chkndfree(tmp_module);
					continue;
				}
				if(!sipxmpp_modules)
				{
					sipxmpp_modules = tmp_module;
				}
				else
				{
					struct sipxmpp_module *loop_module = sipxmpp_modules;
					while(loop_module->next)
						loop_module = loop_module->next;
					loop_module->next = tmp_module;
				}
				(* module_init)();
			}
			else
			{
				LogD("[%s] is not a module. type=[%d]",file->d_name,file->d_type);
			}
		}
		closedir(dir);
	}
	else
	{
		LogE("can not open modules dir.");
	}
	return 0;

}
static void unload_modules()
{
	int (* mod_unload)();
	struct sipxmpp_module *tmp_module;
	while(sipxmpp_modules)
	{
		tmp_module = sipxmpp_modules->next;
		mod_unload = dlsym(sipxmpp_modules->handle, "mod_unload");
		(* mod_unload)();
		dlclose(sipxmpp_modules->handle);
		chkndfree(sipxmpp_modules);
		sipxmpp_modules = tmp_module;
	}
}
static void reload_modules()
{
	int (* mod_reload)();
	struct sipxmpp_module *tmp_module = sipxmpp_modules;
	while(tmp_module)
	{
		mod_reload = dlsym(tmp_module->handle, "mod_reload");
		(* mod_reload)();
		tmp_module = tmp_module->next;
	}
}
int main(int argc, char *argv[])
{
	sipxmppgate_stopping = 0;
	char cwd[255];
	pid_t pid, sid;
	int i;
	char *path = getcwd(cwd,sizeof(cwd));
	if(xmpp_getopt(argc, argv))
			return help();

	if(sipxmpp_log_syslog)
		openlog("sipxmppserver", LOG_PID, LOG_USER);
	if(getppid() == 1)
	{
		LogE("already a daemon. exit");
		return 0;
	}
	if(daemon_mode)
	{
		pid = sipxmpp_fork();

		if (pid < 0) exit(EXIT_FAILURE);
		if (pid > 0) exit(EXIT_SUCCESS);
		sid = setsid();
		if (sid < 0) exit(EXIT_FAILURE);

		for (i=getdtablesize();i>=0;--i) close(i); /* close all descriptors */
		open("/dev/null",O_RDWR); dup(i); dup(i); /* handle standart I/O */
		umask(027);

		if ((chdir(cwd)) < 0) exit(EXIT_FAILURE);
	}
	//create pid file.
	if(pid_file)
	{
		FILE *f = fopen(pid_file, "w");
		if (f) {
			fprintf(f, "%ld\n", (long)getpid());
			fclose(f);
		}
	}
	signal(SIGCHLD,SIG_IGN); /* ignore child */
	signal(SIGTSTP,SIG_IGN); /* ignore tty signals */
	signal(SIGTTOU,SIG_IGN);
	signal(SIGTTIN,SIG_IGN);

	signal(SIGHUP, reload_config);
	signal(SIGINT, ex_program);
	signal(SIGTERM, ex_program);

#ifdef MEMORY_LEAK_WATCHER
		memory_watcher_init();
#endif
	load_modules();
	micro_libxml_init();
	sipxmpp_init();
	Log("starting sip server......");
	init_sip_server();
	Log("sip server started.......");
	while(!sipxmppgate_stopping)
	{
		if(0)//if(detect_config_change() == 1)
		{
			LogI("config file changed. unload sip_server");
			destroy_sip_server();
			LogI("loading sip_server");
			init_sip_server();
		}
		sleep(10);
	}
	destroy_sip_server();
	sipxmpp_destroy();
	micro_libxml_destroy();
	unload_modules();
	Log("destroy success.");
#ifdef MEMORY_LEAK_WATCHER
	memory_watcher_destroy();
#endif
	exit(0);
}
